﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ADSync
{
	public class GroupManager : IDisposable
	{
		public Dictionary<string, GroupDetails> groups;
		private string[] managedgroups;
		private ADToolKit toolkit;

		public List<ActionDetails> Log { get { return toolkit.ActionLog; } }

		public GroupManager(IGrouping<string, string>[] g, bool act)
		{
			groups = new Dictionary<string, GroupDetails>(g.Length);
			foreach (var i in g)
				groups[i.Key] = new GroupDetails(i.ToArray());

			toolkit = new ADToolKit(act);
		}

		/// <summary>
		/// Query AD and find all group members for these groups
		/// </summary>
		public void PopulateManagedGroupList(IEnumerable<string> mgroups)
		{
			managedgroups = mgroups.ToArray();

			foreach (string g in managedgroups)
			{
				GroupDetails det;
				if (!groups.TryGetValue(g, out det))
				{
					det = new GroupDetails(new string[] { });
					groups[g] = det;
				}
				det.admembers = toolkit.QueryGroupMembers(g).ToArray();
			}

		}

		/// <summary>
		/// Add all groups required. Existing groups will not make errors. Returns a list of new groups created
		/// </summary>
		public string[] AddNewGroups()
		{
			List<string> newgroups;
			toolkit.CreateGroups(GetNonADGroupNames(), out newgroups);
			return newgroups.ToArray();
		}

		/// <summary>
		/// Add all group memberships. Existing memberships will not make errors
		/// </summary>
		public int AddGroupMemberships()
		{
			int c = 0;

			foreach (var g in groups)
			{
				List<string> u = g.Value.GetNewMembers();
				if (u.Count > 0)
					c += toolkit.AddUsersToGroup(u, g.Key);
			}

			return c;
		}

		/// <summary>
		/// Check managed groups: do they have any members not required in db. If not, remove those memberships
		/// </summary>
		public int RemoveUnneededGroupMembers()
		{
			int c = 0;

			foreach (string g in managedgroups)
			{
				List<string> remove = groups[g].GetMembersToDelete();

				if (remove.Count > 0)
					c += toolkit.RemoveUsersFromGroup(remove, g);
			}

			return c;
		}

		/// <summary>
		/// Extract group names from IGrouping array
		/// </summary>
		public IEnumerable<string> GetGroupNames()
		{
			foreach (var g in groups)
				yield return g.Key;
		}

		/// <summary>
		/// Yields a list of groups that dont exist in AD, but need users from db - for creation
		/// </summary>
		public IEnumerable<string> GetNonADGroupNames()
		{
			foreach (KeyValuePair<string, GroupDetails> kv in groups)
			{
				if (kv.Value.admembers == null || kv.Value.admembers.Length == 0)
					if (kv.Value.neededmembers != null && kv.Value.neededmembers.Length > 0)
						yield return kv.Key;
			}
		}

		public string GetLogText(bool errorstatus)
		{
			StringBuilder b = new StringBuilder(5000);
			bool found = false;

			foreach (ActionDetails e in Log)
			{
				if (e.Error != errorstatus) continue;

				b.Append(e.Action);
				b.Append(", User: ");
				b.Append(e.User);
				b.Append(", group: ");
				b.Append(e.Group);
				b.Append("\r\n");
				found = true;
			}

			if (!found) b.Append("None\r\n");

			return b.ToString();
		}

		public void Dispose()
		{
			try
			{
				if (toolkit != null)
					toolkit.Dispose();
			}
			finally
			{
				toolkit = null;
			}
		}
	}

	public static class AcYear
	{
		public static int AsInt(DateTime d)
		{
			int year = d.Year - 2000;
			if (d.Month < 8) --year;
			return year;
		}

		public static int CurrentAsInt()
		{
			return AsInt(DateTime.Now);
		}

		public static string AsString(DateTime d)
		{
			int year = d.Year;
			if (d.Month < 8) --year;
			return year.ToString().Substring(2);
		}

		public static string CurrentAsString()
		{
			return AsString(DateTime.Now);
		}
	}

	/// <summary>
	/// Group-User membership record - only used for Linq and then stripped into a string,string[] dictionary
	/// </summary>
	public struct Membership
	{
		public string Group;
		public string User;

		public Membership(string g, string u)
		{
			Group = g;
			User = u;
		}

		/// <summary>
		/// Does supplied array contain this user?
		/// </summary>
		public static bool ContainsUser(Membership[] mems, string u)
		{
			if (mems == null || mems.Length == 0) return false;

			foreach (Membership m in mems)
				if (m.User == u)
					return true;

			return false;
		}
	}

	public class GroupDetails
	{
		public string[] neededmembers;
		public string[] admembers;

		public GroupDetails(string[] needed, string[] ad)
		{
			neededmembers = needed;
			admembers = ad;
		}

		public GroupDetails(string[] needed)
		{
			neededmembers = needed;
			admembers = null;
		}

		/// <summary>
		/// Members in AD who need deleting according to db
		/// </summary>
		public List<string> GetMembersToDelete()
		{
			List<string> r = new List<string>();
			foreach (string user in admembers)
				if (neededmembers == null || !neededmembers.Contains(user))
					r.Add(user);

			return r;
		}

		/// <summary>
		/// Members required according to db, who are not in AD
		/// </summary>
		public List<string> GetNewMembers()
		{
			List<string> r = new List<string>();
			foreach (string mem in neededmembers)
				if (admembers == null || !admembers.Contains(mem))
					r.Add(mem);

			return r;
		}
	}
}
